(function(){
  'use strict';

  angular.module("Yihu.ui", [])

  /**
   * TAB组件
   * 
   * 样例：
   * <tabs>
   *   <pane title="Tab1">
   *     <div>This is tab1 content.</div>
   *    </pane>
   *    <pane title="Tab2">      
   *      <div>This is tab2 content.</div>
   *    </pane>
   * </tabls>
   */
  .directive('tabs', function(){
    return {
      restrict:'E',
      transclude:true,
      scope:{},
      controller:["$scope", function($scope){
        var panels = $scope.panels = [];

        $scope.select = function(panel){
          angular.forEach(panels, function(panel){
            panel.selected = false;
          });
          panel.selected = true;
        };

        this.addPanel = function(panel){
          if(panels.length === 0){
            $scope.select(panel);
          }
          panels.push(panel);
        };
      }],
      template:
        '<div class="tabble">'+
        '<ul class="nav nav-tabs">' +
          '<li ng-repeat="panel in panels" ng-class="{active:panel.selected}">' + 
            '<a href="javascript:;" ng-click="select(panel)" class="J_menuTab"><i class="{{panel.icon}}" ng-if="panel.icon"></i>{{panel.title}}</a>' + 
          '</li>' + 
        '</ul>' + 
        '<div class="tab-content" ng-transclude></div>' + 
        '</div>',
      replace:true
    };
  })

  /**
   * TAB panel, TAB内置Pane指令，依赖tabs
   */
  .directive('panel', function(){
    return {
      require:'^tabs',
      restrict:'E',
      transclude:true,
      scope:{
        title:'@',
        icon:'@'
      },
      link:function(scope, element, attrs, tabsCtrl){
        tabsCtrl.addPanel(scope);
      },
      template: '<div class="tab-pane" ng-class="{active:selected}" ng-transclude></div>',
      replace:true
    };
  })

  .directive('iconlist', function(){
    return {
      restrict:'AE',
      transclude:true, 
      replace:true,
      template:'<div class="iconview"><div class="viewholder"><a ng-repeat="icon in icons" data-id="{{icon.id}}" href="javascript:void(0)" class="fl whiteblock"><button class="close closeBtn" ng-click="removeIcon(icon)">&times;</button><img ng-src="{{icon.link}}" ng-click="viewImage($index)" width="150" border="0"></a></div></div>',
      link:function(scope, element, attrs){
        var watcher = scope.$watch('icons.length', function(newValue, oldValue){
          if(newValue){
            $(".iconview .viewholder").css("width", (scope.icons.length+2) * 152 + "px");
          }
        });
      }
    };
  })

  .directive('bannerlist', function(){
    return {
      restrict:'AE',
      transclude:true, 
      replace:true,
      template:'<div class="iconview"><div class="viewholder"><a ng-repeat="icon in icons" data-id="{{icon.id}}" href="javascript:void(0)" class="fl whiteblock"><button class="close closeBtn" ng-click="removeIcon(icon)">&times;</button><img ng-src="{{icon.link}}" ng-click="viewImage($index)" height="180" border="0"></a></div></div>',
      link:function(scope, element, attrs){
        // var watcher = scope.$watch('icons.length', function(newValue, oldValue){
        //   if(newValue){
        //     $(".iconview .viewholder").css("width", (scope.icons.length) * 560 + "px");
        //   }
        // });
      }
    };
  })

  .directive("imagepreview", ['dateFilter', function(dateFilter){
    return {
      restrict: 'AE',
      transclude: true,
      replace: true,
      templateUrl: 'template/modal/imageDialog.html',
      link:function(scope, element, attrs){
        scope.closed = true;
        // scope.images = [{
        //   link: 'upload/56ebe501Nba62be22_big.jpg',
        //   title: '志林姐姐',
        //   directory: '我的图片',
        //   createdAt: dateFilter(new Date(), 'yyyy-MM-dd HH:mm:ss')
        // },{
        //   link: 'upload/56cd53bfN8a702a64_big.jpg',
        //   title: '志林姐姐',
        //   directory: '我的图片',
        //   createdAt: dateFilter(new Date(), 'yyyy-MM-dd HH:mm:ss')
        // }];
        var images = scope.images || [];
        var idx = scope.currentIndex = 0;
        var watcher = scope.$watch('previewLoaded', function(newVal, oldVal){
          if(newVal){
            watcher();
            getImage();
          }
        });

        scope.reloadImage = function(imgs){
          images = scope.images = imgs;
        };

        function getImage(){
          $("#img-loader").show();
          $("#view-image").hide().removeAttr("style");
          scope.img = images.length ? images[scope.currentIndex] : {};
          scope.img.loaded = false;          
          setTimeout(recalc, 0);
        }

        scope.viewImage = function(idx){
          scope.setIndex(idx);
          scope.closed = false;
        };

        scope.viewImageUrl = function(url){
          if(!!scope.disablePrevied){
            scope.closed = true;
            return;
          }
          for(var i=0; i<images.length; i++){
            if(images[i].link.indexOf(url)>=0){
              scope.setIndex(i);
            }
          }
          scope.closed = false;
        };

        scope.setIndex = function(idx){
          scope.currentIndex = idx || 0;
          getImage();
        };

        scope.hasPrev = function(){
          return scope.currentIndex > 0;
        };

        scope.hasNext = function(){
          return scope.currentIndex < images.length-1;
        };

        scope.closeDialog = function(){
          scope.closed = true;
        };

        scope.prev = function(){
          if(scope.currentIndex > 0){
            scope.currentIndex--;
          }else{
            scope.currentIndex = images.length-1;
          }
          getImage();
        };

        scope.next = function(){
          if(scope.currentIndex < images.length-1){
            scope.currentIndex++;
          }else{
            scope.currentIndex = 0;
          }
          getImage();
        };

        scope.hover = function(){
          $(".img-nav span").toggle();
        };

        window.onresize = function(){
          recalc();
        };

        function recalc(){
          var imgWrap = $(".img-wrap");
          if(imgWrap.length === 0){
            $("#img-loader").hide();
            return;
          }
          var width = imgWrap.outerWidth();
          var height = imgWrap.outerHeight();
          var w = imgWrap.width();
          var h = imgWrap.height();

          var p = scope.img || {};
          var pic = $("#view-image");
          var pw = p.width || pic.get(0).offsetWidth;
          var ph = p.height || pic.get(0).offsetHeight;

          var deltaW = w / pw;
          var deltaH = h / ph;
          var delta = deltaW;
          if(deltaW > deltaH){
            delta = deltaH;
          }

          var newpw = delta>1 ? pw : delta * pw;
          var newph = delta>1 ? ph : delta * ph;
          var offsetLeft = (width - newpw) / 2;
          var offsetTop = (height - newph) / 2;

          pic.unbind('load').bind('load', function(){
            $("#img-loader").hide();
            scope.$apply(function(){
              scope.img.loaded = true;
            });
          });
          setTimeout(function(){
            if(scope.img && !scope.img.loaded){
              $("#img-loader").hide();
              scope.$apply(function(){
                scope.img.loaded = true;
              });
            }
          }, 1000);
          pic.css({
            top: offsetTop + 'px',
            left: offsetLeft + 'px',
            width: newpw + 'px',
            height: newph + 'px',
            transform: 'rotate(0deg)',
//            transition: 'opacity 1s',
            opacity: 1
          }).show();
        }
      }
    };
  }])

  /** 
   * 左右多选列表组件
   */
  .directive('multichoice', function(){
    return {
      restrict: 'AE',
      replace: true, 
      transclude: true, 
      template:'<div class="multi-choice">'+
                '<div class="left">'+
                  '<p ng-bind="left.title"></p>'+
                  '<select name="leftItem" class="form-control" ng-model="left.item" multiple="multiple">'+
                   // ng-options="item.text as item.text for item in left.items">'+
                    '<option ng-repeat="item in left.items" value="{{item.value}}">{{item.text}}</option>'+
                  '</select>'+
                '</div>'+
                '<div class="middle">'+
                  '<div class="btn-group">'+
                    '<button class="btn btn-primary splitter" ng-click="assign()"><i class="fa fa-angle-right"></i></button> '+
                    '<button class="btn btn-primary" ng-click="revoke()"><i class="fa fa-angle-left"></i></button> '+
                  '</div>'+
                '</div>'+
                '<div class="right">'+
                  '<p ng-bind="right.title"></p>'+
                  '<select name="rightItem" class="form-control" ng-model="right.item" multiple="multiple">'+
                    '<option ng-repeat="item in right.items" value="{{item.value}}">{{item.text}}</option>'+
                  '</select>'+
                '</div>'+
              '</div>'+
            '</div>', 
      link:function(scope, element, attrs){
        scope.assign = function(){
          var items = scope.left.items;
          $("select[name='leftItem'] option:selected").map(function(){
            var item = $(this);
            var obj = { text: item.text(), value: item.val() };
            scope.right.items.push(obj);

            for(var i=0; i<items.length; i++){
              if(items[i].value == item.val()){
                items.splice(i, 1);
              }
            }
          });

          setTimeout(function(){
            var nullItem = $("select[name='leftItem'] > option:eq(0)");
            if(nullItem.length && nullItem.val().indexOf('?')>=0){
              nullItem.remove();
            }
          }, 50);
        };

        scope.revoke = function(){
          var items = scope.right.items;
          $("select[name='rightItem'] option:selected").map(function(){
            var item = $(this);
            var obj = { text: item.text(), value: item.val() };
            scope.left.items.push(obj);

            for(var i=0; i<items.length; i++){
              if(items[i].value == item.val()){
                items.splice(i, 1);
              }
            }
          });

          setTimeout(function(){
            var nullItem = $("select[name='rightItem'] > option:eq(0)");
            if(nullItem.length && nullItem.val().indexOf('?')>=0){
              nullItem.remove();
            }
          }, 50);
        };
      }
    };
  })

  /**
   * Login登录指令，模板页面可订制
   *
   * 样例：
   * <login></login>
   */
  .directive('login', function(){
    return {
      restrict:'E',
      templateUrl:'/tpls/user/login.html',
      replace:true,
      transclude:true
    };
  })

  /**
   * Sets focus to this element if the value of focus-me is true.
   * @example
   *  <a ng-click="addName=true">add name</a>
   *  <input ng-show="addName" type="text" ng-model="name" focus-me="{{addName}}" />
   */
  .directive('focusMe', ['$timeout', function($timeout) {
    return {
      scope: { trigger: '@focusMe' },
      link: function(scope, element) {
        scope.$watch('trigger', function(value) {
          if(value === "true") {
            $timeout(function() {
              element[0].focus();
            });
          }
        });
      }
    };
  }])

  /**
   * Two-way data binding for contenteditable elements with ng-model.
   * @example
   *   <p contenteditable="true" ng-model="text"></p>
   */
  .directive('contenteditable', function() {
    return {
      require: '?ngModel',
      link: function(scope, element, attrs, ctrl) {
        // Do nothing if this is not bound to a model
        if (!ctrl) { return; }

        // Checks for updates (input or pressing ENTER)
        // view -> model
        element.bind('input enterKey', function() {
          var rerender = false;
          var html = element.html();

          if (attrs.noLineBreaks) {
            html = html.replace(/<div>/g, '').replace(/<br>/g, '').replace(/<\/div>/g, '');
            rerender = true;
          }

          scope.$apply(function() {
            ctrl.$setViewValue(html);
            if(rerender) {
              ctrl.$render();
            }
          });
        });

        element.keyup(function(e){
          if(e.keyCode === 13){
            element.trigger('enterKey');
          }
        });

        // model -> view
        ctrl.$render = function() {
          element.html(ctrl.$viewValue);
        };

        // load init value from DOM
        ctrl.$render();
      }
    };
  })

  /** 
   * 表单整数指令
   *
   * 样例：
   * <input type="number" integer name="num1"/>
   * 非整数则返回验证失败，使用form.num1.$invalid显示隐藏错误提示
   */
  .directive('integer', function(){
    var INTEGER_REGEXP = /^\-?\d*$/;
    return {
      require : 'ngModel',
      link : function(scope, elm, attrs, ctrl) {
        ctrl.$parsers.unshift(function(viewValue) {
          if (INTEGER_REGEXP.test(viewValue)) {
            ctrl.$setValidity('integer', true);
            return viewValue;
          } else {
            ctrl.$setValidity('integer', false);
            return undefined;
          }
        });
      }
    };
  })

  /**
   * 表单浮点数指令
   * 
   * 样例：
   * <input type="number" smartfloat name="num1"/>
   * 非浮点数则返回验证失败，使用form.num1.$invalid显示隐藏错误提示
   */
  .directive('smartfloat', function(){
    var FLOAT_REGEXP = /^\-?\d+((\.|\,)\d+)?$/;
    return {
      require : 'ngModel',
      link : function(scope, elm, attrs, ctrl) {
        ctrl.$parsers.unshift(function(viewValue) {
          if (FLOAT_REGEXP.test(viewValue)) {
            ctrl.$setValidity('float', true);
            return parseFloat(viewValue.replace(',','.'));
          } else {
            ctrl.$setValidity('float', false);
            return undefined;
          }
        });
      }
    };
  })

  /**
   * 表单远程验证有效性
   *
   * <input type="text" name="username" remotecheck /> * 
   * 服务端返回验证失败，使用form.username.$invalid显示隐藏错误提示
   */
  .directive('remotecheck', ['$http',function($http){
    return {
      require : 'ngModel',
      link : function(scope, element, attrs, ctrl) {
        scope.$watch(attrs.ngModel, function () {
          var text = element.val();
          if(text){
            $http({
              method: 'POST', 
              url: '/api/check/' + attrs.name,
              data:{'field' : element.val()}
            }).success(function(data, status, headers, config) {
              ctrl.$setValidity('remote', data.result);
            }).error(function(data, status, headers, config) {
              ctrl.$setValidity('remote', false);
            }); 
          }
        });
      }
    };
  }])

  //面包屑导航
  .directive('breadcrumb', function(){
    return {
      restrict:'AE',
      template:
        '<ol class="breadcrumb">'+
          '<li ng-repeat="item in items">'+
            '<span ng-hide="item.link" ng-if="!item.link" ng-bind="item.text"></span>'+
            '<a ng-if="item.link" ng-show="item.link" ui-sref="{{item.link}}">{{item.text}}</a>'+
          '</li>'+
        '</ol>',
      transclude:true,
      replace:true
    };
  })

  //单选，复选指令， 基于icheck插件
  .directive('icheck', ['$timeout','$parse', function($timeout, $parse) {
    return {
      require: 'ngModel',
      link: function($scope, element, $attrs, ngModel) {
        // $ocLazyLoad.load(['libs/iCheck/skins/square/green.css', 'libs/iCheck/icheck.min.js'])
        // .then(function(){
          var timer = $timeout(function(){
            $scope.$watch($attrs.ngModel, function(newValue){
              $(element).iCheck('update');
            });

            $scope.$watch($attrs['ngDisabled'], function (newValue) {
              $(element).iCheck(newValue ? 'disable' : 'enable');
              $(element).iCheck('update');
            });

            return $(element).iCheck({
             checkboxClass: 'icheckbox_square-green',
             radioClass: 'iradio_square-green'
            }).on('ifChanged', function(event) {
              if ($(element).attr('type') === 'checkbox' && $attrs.ngModel) {
                $scope.$apply(function() {
                  return ngModel.$setViewValue(event.target.checked);
                });
              }
            }).on('ifClicked', function(event){
              if ($(element).attr('type') === 'radio' && $attrs.ngModel) {
                $scope.$apply(function() {
                  //set up for radio buttons to be de-selectable
                  var value = event.target.value || 'false';
                  if (ngModel.$viewValue != value){
                    return ngModel.$setViewValue(value);
                  } else{
                    ngModel.$setViewValue(null);
                  }
                  ngModel.$render();
                  return;
                  //return ngModel.$setViewValue($(element).prop('checked'));
                });
              }
            });
          }, 50);
        // });

        $scope.$on('$destroy', function(){
          $timeout.cancel(timer);
        });
      }
    };
  }])

  //日期选择指令， 基于Laydate
  .directive('idate', ['$timeout', function($timeout){
    return {
      require:'ngModel',
      restrict:'A',
      link:function(scope, element, attrs, ngModel){
        var timer = $timeout(function(){
          laydate.render({
            elem:'#'+$(element).attr('id'),
            theme: 'molv',
            position: 'fixed',
            format: 'yyyy-MM-dd', //日期格式
            done: function(value, date){ //选择好日期的回调 
              scope.$apply(function() {
                return ngModel.$setViewValue(date);
              });
            }
          });
        }, 50);

        scope.$on('$destroy', function(){
          $timeout.cancel(timer);
        });

      }
    };
  }])

  //日期时间选择指令， 基于Laydate
  .directive('itime', ['$timeout', function($timeout){
    return {
      require:'ngModel',
      restrict:'A',
      link:function(scope, element, attrs, ngModel){
        var timer = $timeout(function(){
          laydate.render({
            elem:'#'+$(element).attr('id'),
            theme: 'molv',
            type: 'datetime',
            position: 'fixed',
            done: function(value, date){ //选择好日期的回调 
              scope.$apply(function() {
                return ngModel.$setViewValue(date);
              });
            }
          });

        }, 50);

        scope.$on('$destroy', function(){
          $timeout.cancel(timer);
        });

      }
    };
  }])

  //日期时间选择指令yyyy-MM-dd HH:mm， 基于Laydate
  .directive('itime2', ['$timeout', function($timeout){
    return {
      require:'ngModel',
      restrict:'A',
      link:function(scope, element, attrs, ngModel){
        var timer = $timeout(function(){
          laydate.render({
            elem:'#'+$(element).attr('id'),
            theme: 'molv',
            type: 'datetime',
            format: 'yyyy-MM-dd HH:mm',
            position: 'fixed',
            done: function(value, date){ //选择好日期的回调 
              scope.$apply(function() {
                return ngModel.$setViewValue(date);
              });
            }
          });

        }, 50);

        scope.$on('$destroy', function(){
          $timeout.cancel(timer);
        });

      }
    };
  }])

  //确认密码检查一致性指令
  .directive('pwCheck', function(){
    return {
      require:'ngModel',
      link:function(scope, element, attrs, ctrl){
        var pwd = '#'+attrs.pwCheck;
        element.add(pwd).on('keyup', function(){
          scope.$apply(function(){
            var match = element.val()===$(pwd).val();
            ctrl.$setValidity('pwmatch', match);
          });
        });
      }
    };
  })

  //文件管理列表显示指令
  .directive("filegrid", function(){
    return {
      restrict:'AE',
      template:'<div class="file-box" ng-if="gridData.files.length" ng-repeat="item in gridData.files">'+
        '<div class="file">'+
          '<div class="item-select" ng-if="!item.creating" title="{{item.fileName}}" data-type="{{item.fileType}}" data-id="{{item.id}}" data-index="{{$index}}" data-picture="{{item.picture.id}}" data-width="{{item.picture.width}}" data-height="{{item.picture.height}}" data-thWidth-width="{{item.picture.thWidth.width}}" data-thWidth-height="{{item.picture.thWidth.height}}" data-thHeight-width="{{item.picture.thHeight.width}}" data-thHeight-height="{{item.picture.thHeight.height}}">'+
            '<i class="icon-check checkbox"></i>'+
          '</div>'+
          '<a href="javascript:void(0)" ng-click="fireAction(item)" title="文件名：{{item.fileName}}&#10;标签：图片">'+
            '<div class="image" ng-if="item.fileType==\'image\'">'+
              '<img alt="" class="img-responsive" ng-if="item.picture" ng-src="{{downloadUrl}}{{item.picture.id}}?w=136" ng-click=\'viewImageUrl(item.picture.id)\' data-width="{{item.picture.width}}" data-height="{{item.picture.height}}">'+
              '<i ng-if="!item.picture" class="folder-icons cloudfile"></i>'+
            '</div>'+
            '<div class="icon" ng-if="item.fileType==\'video\' || item.fileType==\'audio\'">'+
              '<img alt="" class="img-responsive" ng-if="item.picture.id" ng-src="{{downloadUrl}}{{item.picture.id}}?w=136">' +
              '<i class="play-icon"></i>'+
            '</div>'+
            '<div class="image" ng-if="item.fileType==\'file\'">'+
              '<i class="folder-icons cloudfile"></i>'+
            '</div>'+
            '<div class="icon" ng-if="item.fileType==\'folder\'">'+
              '<i class="folder-icons folder"></i>'+
            '</div>'+
            '<span class="corner"></span>'+
          '</a>'+
          '<div class="file-name">'+
            '<div ng-if="item.creating" class="create-folder"><input class="form-control" onclick="this.select()" ng-model="item.fileName" ng-keypress="doCreateFolder($event, item)">' +
            '<div class="btn-group"><a class="btn btn-sm btn-primary" href="javascript:void(0)" ng-click="fireCreateFolder(item)">确定</a><a class="btn btn-sm btn-danger" href="javascript:void(0)" ng-click="discardFolder(item)">取消</a></div></div>' +
            '<small ng-if="!item.creating">{{item.fileName}}</small>'+
          '</div>'+
        '</div>'+
      '</div>',
      transclude:true,
      replace:true
    };
  })

  //文件管理器面包屑导航指令
  .directive('opbreadcrumb', function(){
    return {
      restrict:'AC',
      link:function($scope, elements, attrs){
        var watcher = $scope.$watch('nav.current', function(newValue, oldValue){
          var breadcrumbs = $scope.breadcrumbs;
          var opPrev = $("#op-prev");
          if(breadcrumbs.length==1){
            opPrev.hide();
          }else if(breadcrumbs.length>1){
            opPrev.show();
          }
        });

        $scope.$on("$destroy", function(){
          watcher();
        });
      }
    };
  })

  //文件管理器类别选择指令
  .directive('optype', function(){
    return {
      restrict:'AC',
      link:function(scope, element, attrs){
        element.on('click', function(){
          $(element).addClass('btn-primary').attr('checked','checked').siblings().removeClass('btn-primary').removeAttr('checked');
        });
      }
    };
  })

  //扩展textarea
  .directive('mytextarea', function() {
    return {
      restrict: 'AE',
      link: function(scope, ele, attrs, ctrl) {
        scope.$on('textarea.update', function(event, data){
          if (data && attrs.id === data.id) {
            ctrl.$setViewValue(data.text);
            ctrl.$render();
            ele.innerText = data.text;
          }
        });
      }
    };
  })

  //自动保存插件
  .directive("autosave", ['$timeout','dateFilter','BasicService','Store', function($timeout, dateFilter, BasicService, Store){
    return {
      restrict: 'AE',
      replace: true,
      transclude:true,
      link:function(scope, elements, attrs){
        var module = attrs.module, saveId, saveKey, dbKey; 

        var timer, lastContent,
            lastSaveTime = new Date(), 
            intervalTime = parseInt(attrs.interval,10) || 10000, 
            format = 'yyyy-MM-dd HH:mm:ss';

        scope.$on('editor.update', function(event, data){
          if(data){
            save(data);
          }
        });

        scope.$on('clean.autosave', function(event, data){
          saveId = attrs.saveid;
          saveKey = "autosave-"+module+(saveId || '');
          Store.remove(saveKey);
        });

        function save(contents){
          if(lastContent == contents){
            return;
          }

          if(new Date() - lastSaveTime < intervalTime){
            return;
          }
          if(timer){
            $timeout.cancel(timer);
          }


          saveId = attrs.saveid;
          saveKey = "autosave-"+module+(saveId || '');
          dbKey = module + '-'+ (saveId || lastSaveTime.getTime()) + '-draft';

          lastContent = contents;
          lastSaveTime = new Date();
          timer = $timeout(function(){
            Store.save(saveKey, dbKey, 24*3600000);

            var form = {
              autoKey: dbKey,
              content: contents
            };
            BasicService.autoSaving({}, form, function(res){
              if(res.result === 0){
                $("#autoSaver").html('内容已自动保存，上一次保存时间：'+ dateFilter(new Date(), format));
              }else{
                $("#autoSaver").html('自动保存失败, 等待下次执行！');
              }
            });
          }, 2000);
        }

        scope.$on("$destroy", function(){
          $timeout.cancel(timer);
        });
      },
      template: '<div id="autoSaver"></div>'
    };
  }])

  //自动保存插件
  .directive("autosaver", ['$timeout','dateFilter','BasicService','Store', function($timeout, dateFilter, BasicService, Store){
    return {
      restrict: 'AE',
      replace: true,
      transclude:true,
      link:function(scope, elements, attrs){
        var module = attrs.module, saveId, saveKey, dbKey; 

        var timer, lastContent,
            lastSaveTime = new Date(), 
            intervalTime = parseInt(attrs.interval,10) || 10000, 
            format = 'yyyy-MM-dd HH:mm:ss';

        scope.$on('editor.update', function(event, data){
          if(data){
            save(data);
          }
        });

        scope.$on('clean.autosave', function(event, data){
          saveId = attrs.saveid;
          saveKey = "autosave-"+module+(saveId || '');
          Store.remove(saveKey);
        });

        function save(contents){
          if(lastContent == contents){
            return;
          }

          if(new Date() - lastSaveTime < intervalTime){
            return;
          }
          if(timer){
            $timeout.cancel(timer);
          }


          saveId = attrs.saveid;
          saveKey = "autosave-"+module+(saveId || '');
          dbKey = module + '-'+ (saveId || lastSaveTime.getTime()) + '-draft';

          lastContent = contents;
          lastSaveTime = new Date();
          timer = $timeout(function(){
            Store.save(saveKey, dbKey, 24*3600000);

            var form = {
              autoKey: dbKey,
              content: contents
            };
            BasicService.autoSaving({}, form, function(res){
              if(res.result === 0){
                $("#" + attrs.id).html('内容已自动保存，上一次保存时间：'+ dateFilter(new Date(), format));
              }else{
                $("#" + attrs.id).html('自动保存失败, 等待下次执行！');
              }
            });
          }, 2000);
        }

        scope.$on("$destroy", function(){
          $timeout.cancel(timer);
        });
      },
      template: '<div name="autoSaver"></div>'
    };
  }])

  //标签指令
  .directive('tags', ['TagService', function(TagService){
    return {
      restrict:'AE',
      template:'<dl id="tagsList" class="form-control w350"><dt>所有标签</dt><dt class="closeBtn close" title="关闭">&times;</dt><div><dd ng-repeat="tag in tags | filter:queryTag" data-weight="{{tag.weight}}">{{tag.text}}</dd></div></dl>',
      transclude:true,
      replace:true,
      link:function(scope, element, attrs){
        // var tags = scope.selectedTags || "";
        var item = $('#'+attrs.elemid);
        item.wrap($('<div class="form-tags"></div>'));

        var watcher = scope.$watch("selectedTags", function(newVal, oldVal){
          if(newVal || newVal === ''){
            watcher();

            var tags = (scope.selectedTags  && scope.selectedTags.split(' ')) || [];
            for(var i=0; i<tags.length; i++){
              addTag(tags[i]);
              initTagClass(tags[i]);
            }
          }
        });

        function initTagClass(tag){
          $("#tagsList dd").each(function(i, item){
            if(tag == $(item).text()){
              $(item).addClass('active');
              return;
            }
          });
        }

        function addTag(text, weight){
          weight = weight || 1;
          var el = $(".form-tags>span:contains('"+text+"')").remove();
          if(el && el.length===0){
            var tag = $('<span class="label label-info" data-weight="' +　weight +'">' + text + '<i class="fa fa-times-circle"></i></span>').css("cursor","pointer");
            item.before(tag);
            tag.click(removeTag);
          }
        }

        function getTagsAsString(){
          return $(".form-tags>span").map(function(){ return $(this).text().replace('×', '');}).get().join(" ");
        }

        $("#tagsList .closeBtn").bind('touchstart click', function(e){
          e.preventDefault();
          $(element).toggle();
        });

        function removeTag(){
          var text = $(this).text();
          $(this).remove();
          $("#tagsList dd").each(function(i, item){
            if(text.indexOf($(item).text())>=0){
              $(item).toggleClass('active');
            }
          }); 

          scope.$apply(function(){
            scope.selectedTags = getTagsAsString();
          });
          $(element).hide();
        }

        item.keypress(function(event){   
          event.preventDefault();       
          var self = this;
          var keycode = event.keyCode || event.which;
          if(keycode  != '13'){
            return;
          }

          var text = $(this).val();
          if(!text || $.inArray(text, scope.selectedTags.split(' ')) >= 0){
            return;
          }

          addTag(text);
          var form = {
            name: text,
            weight: 1,
            user: {
              id: scope.currentUser.userId,
              name: scope.currentUser.userName
            }
          };
          TagService.create({}, form, function(data){
            scope.$emit("updateTag");
          });
          
          $("#tagsList dd").each(function(i, item){
            if(text.indexOf($(item).text())>=0){
              $(item).toggleClass('active');
            }
          }); 
          $(self).val('');
          $(element).hide();

          scope.$apply(function(){
            scope.selectedTags = getTagsAsString();
            scope.queryTag = '';
          });
        }); 

        item.click(function(ev){
          ev.preventDefault();
          $(element).css({
            "position":"absolute",
            "top":'37px',
            "left": '1em',
            "z-index":100
          }).toggle();

          $("#tagsList dd").unbind('click touchstart').bind('click touchstart', function(e){
            e.preventDefault();

            var val = $(this).text();
            addTag(val, $(this).data('weight'));
            $(this).toggleClass("active");
            scope.$apply(function(){
              scope.selectedTags = getTagsAsString();
            });
          });

        });
        
      }
    };
  }])

  /** 文件管理器选择指令 */
  .directive('checkbox', function(){
    return {
      restrict:'AC',
      link:function(scope, element, attrs){
        var id = $(element).attr("id");        
        $(element).click(function(){
          var checkNum = scope.opbar && scope.opbar.checkNum;
          var showToolbar = scope.opbar && scope.opbar.showToolbar;

          if(id=='checkAll'){
            if($(this).parent().hasClass("item-checked")){
              showToolbar = false;
              $(".checkbox").parent().removeClass("item-checked");
              checkNum = 0;
            }else{
              checkNum = $(".checkbox").length-1;
              $(".checkbox").parent().addClass("item-checked");
              showToolbar = true;
            }
          }else{
            $("#checkAll").parent().removeClass("item-checked");
            $(this).parent().toggleClass("item-checked");

            if($(this).parent().hasClass("item-checked")){
              checkNum++;
            }else{
              checkNum--;
            }
            if(checkNum===0){
              showToolbar = false;
            }else{
              showToolbar = true;
            }
            if(checkNum==$(".checkbox").length-1){
              $("#checkAll").parent().addClass("item-checked");
            }
          }

          if(scope.opbar){
            scope.$apply(function(){
              scope.opbar.checkNum = checkNum;
              scope.opbar.showToolbar = showToolbar;
            });
          }
        });

      }
    };
  })

  .directive('sidebar', function($window, $document, $compile){
    return {
      restrict: 'AE',
      // templateUrl: "template/common/sidebar.html",
      link:function($scope, element, attrs, ctrl){
        var elShare = angular.element('<div id="scrollSidebar" class="scrollSidebar"><a href="javascript:void(0)" ng-click="goTop()" id="goTop" title="回到顶部"><i class="glyphicon glyphicon-menu-up"></i></a></div>');

        function showShare() {
          if (!elShare.hasClass('ng-scope')) {
            $compile(elShare)($scope);
            var body = $document.find('body').eq(0);
            body.append(elShare);
          }
          elShare.show();
        }

        var loading = false;
        $scope.goTop = function () {
          loading = true;
          $("html, body").animate({ scrollTop: 0 }, "fast", function () {
            elShare.hide();
            loading = false;
          });
        };

        //http://stackoverflow.com/questions/13549216/changing-css-on-scrolling-angular-style
        var scrollTop = 0;
        var ele = element[0];
        
        element.bind('scroll', function(){
          console.log('handler...');
          if (loading) 
            return;

          scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
          // var screenHeight = document.body.clientHeight || document.documentElement.clientHeight;
          console.log('top: ' + ele.scrollTop + ', scroll: ' + scrollTop);
          showShare();

          console.log($(document).height());
          // if (ele.scrollTop > 50 && scrollTop < ele.scrollTop) {
          // }else {
          //   elShare.hide();
          // }
          scrollTop = document.body.scrollTop;
          $scope.$apply(attrs.sidebar);
        });
      }
    };
  })

  .directive('uploadTask', function(){
    return {
      restrict: 'AE',
      templateUrl: 'template/uploadTask.html',
      link: function(scope, element, attrs){
        scope.minimalDialog = false;

        scope.minDialog = function(){
          scope.minimalDialog = true;
        };

        scope.maxDialog = function(){
          scope.minimalDialog = false;          
        };

        scope.closeUploadDialog = function(){
          scope.showDialog = false;
        };

        scope.openUploadDialog = function(){
          scope.showDialog = true;
        };
      }
    };
  })

  .directive('treeView', function(){
    return {
      restrict: 'E',
      templateUrl: 'tpls/tree/treeView.html',
      scope: {
        treeData: '=',
        canChecked: '=',
        textField: '@',
        itemClicked: '&',
        itemCheckedChanged: '&',
        itemTemplateUrl: '@',
        singleCheck: '='
      },
      controller: function($scope){
        $scope.itemExpanded = function(item, $event){
          item.$$isExpand = !item.$$isExpand;
          $event.stopPropagation();
        };

        $scope.getItemIcon = function(item){
          var isLeaf = $scope.isLeaf(item);
          if(isLeaf){
            return 'fa fa-minus-square-o';  //fa-leaf
          }
          return item.$$isExpand ? 'fa fa-minus-square-o': 'fa fa-plus-square-o';  
        };

        $scope.getFolderIcon = function(item){
          var isLeaf = $scope.isLeaf(item);
          if(isLeaf){
            return 'fa fa-folder-open-o';  //fa-leaf
          }
          return item.$$isExpand ? 'fa fa-folder-open-o': 'fa fa-folder-o'; 
        };

        $scope.isLeaf = function(item){
          return !item.children || !item.children.length;
        };

        $scope.wrapCallback = function(callback, item, $event){
          $scope.selected = item;
          ($scope[callback] || angular.noop)({
            $item:item, $event:$event
          });
        };
      }
    };
  });
 
})();  


/**
 * @license AngularJS v1.2.9
 * (c) 2010-2014 Google, Inc. http://angularjs.org
 * License: MIT
 * Created by Zed on 19-11-2014.
 */
 (function (window, angular) {
  'use strict';

  angular.module('ngKeditor', [])
  .directive('keditor', function () {
    var linkFn = function (scope, elm, attr, ctrl) {
      if (typeof KindEditor === 'undefined') {
        console.error('Please import the local resources of kindeditor!');
        return;
      }
      var watchModel = attr.bindModel;

      var _config = {
        width: '100%',
        autoHeightMode: false,
        afterCreate: function () {
          this.loadPlugin('autoheight');
        }
      };
      var editor;

      var editorId = elm[0],
      editorConfig = scope.config || _config;

      editorConfig.afterChange = function () {
        if (!scope.$$phase) {
          var html = this.html();
          ctrl.$setViewValue(html);

          if(html){
            scope.$emit('editor.update', html);
          }

          // exception happens here when angular is 1.2.28
          // scope.$apply();
        }
      };

      if (KindEditor) {
        editor = KindEditor.create(editorId, editorConfig);
      }
      var watcher = scope.$watch(watchModel, function(newValue, oldValue){
        watcher();

        setTimeout(function(){
          if($(elm).val().indexOf('data-original') > 0) {
            editor.html(Yihu.util.restoreRawHtml($(elm).val()));
          } else {
            editor.html($(elm).val());
          }
        }, 500);
      });

      ctrl.$parsers.push(function (viewValue) {
        ctrl.$setValidity('keditor', viewValue);
        return viewValue;
      });

      scope.$on("$destroy", function(){
        watcher();
      });
    };

    return {
      require: 'ngModel',
      scope: { config: '=config' },
      link: linkFn
    };
  })

  .directive("lazyhtml", function(){
    return {
      restrict:'AE',
      replace:true,
      transclude:true,
      link:function(scope, elements, attrs){
        var bindModel = attrs.bindModel;
        var watcher = scope.$watch(bindModel, function(newValue, oldValue){
          if(newValue){
            watcher();

            setTimeout(function(){
              $('.ke-article').find('.ke-article-header').hide().siblings('.ke-article-body').show();
              $('img.lazy').lazyload({effect:'fadeIn'});
            }, 500);
          }
        });
      }
    };
  });

})(window, window.angular); 